package dao

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"io"
	"net/http"
	"net/url"
	"os"
	"smart-health-platforms/config"
	"smart-health-platforms/inits"
	"smart-health-platforms/pkg"
	"strconv"
	"strings"
	"sync"
	"time"
	"user_srv/model"
)

// GetAccessToken 获取百度云API访问令牌（封装错误处理）
func GetAccessToken() (string, error) {
	url := "https://aip.baidubce.com/oauth/2.0/token"
	postData := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", config.AppCong.Baidu.AK, config.AppCong.Baidu.SK)

	resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(postData))
	if err != nil {
		return "", fmt.Errorf("获取Token失败: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("读取Token响应失败: %w", err)
	}

	var tokenResp map[string]interface{}
	if err := json.Unmarshal(body, &tokenResp); err != nil {
		return "", fmt.Errorf("解析Token响应失败: %w，响应内容: %s", err, string(body))
	}

	// 处理API错误
	if errMsg, ok := tokenResp["error"].(string); ok && errMsg != "" {
		desc, _ := tokenResp["error_description"].(string)
		return "", fmt.Errorf("Token接口报错: %s - %s", errMsg, desc)
	}

	// 提取Token（类型校验）
	accessToken, ok := tokenResp["access_token"].(string)
	if !ok {
		return "", fmt.Errorf("Token格式异常，响应: %s", string(body))
	}

	return accessToken, nil
}

var (
	tokenCache  string
	tokenExpiry time.Time
	tokenMutex  sync.RWMutex
)

// GetAccessTokenWithCache 带缓存的AccessToken获取
func GetAccessTokenWithCache() (string, error) {
	tokenMutex.RLock()
	// 检查缓存是否有效（提前5分钟刷新）
	if tokenCache != "" && time.Now().Before(tokenExpiry.Add(-5*time.Minute)) {
		defer tokenMutex.RUnlock()
		return tokenCache, nil
	}
	tokenMutex.RUnlock()

	// 获取新Token
	tokenMutex.Lock()
	defer tokenMutex.Unlock()

	// 双重检查，防止多个goroutine同时刷新
	if tokenCache != "" && time.Now().Before(tokenExpiry.Add(-5*time.Minute)) {
		return tokenCache, nil
	}

	newToken, err := GetAccessToken()
	if err != nil {
		return "", err
	}

	// 百度Token通常有效期为30天，这里设置为29天避免过期
	tokenCache = newToken
	tokenExpiry = time.Now().Add(29 * 24 * time.Hour)

	return newToken, nil
}

// MatchRequest 人脸比对请求参数（支持1-10张人脸）
type MatchRequest []struct {
	Image           string `json:"image"`            // 人脸Base64编码
	ImageType       string `json:"image_type"`       // 固定为"BASE64"
	QualityControl  string `json:"quality_control"`  // 图片质量控制（如"NORMAL"）
	LivenessControl string `json:"liveness_control"` // 活体检测控制（如"NONE"）
}

// MatchResponse 人脸比对响应结果
type MatchResponse struct {
	ErrorCode int    `json:"error_code"`
	ErrorMsg  string `json:"error_msg"`
	LogId     int64  `json:"log_id"`
	Timestamp int64  `json:"timestamp"`
	Result    struct {
		Score     float64 `json:"score"`     // 相似度（0-100）
		Threshold float64 `json:"threshold"` // 推荐匹配阈值
		FaceList  []struct {
			FaceToken string `json:"face_token"` // 人脸特征令牌
		} `json:"face_list"`
	} `json:"result"`
}

// FaceMatch 执行人脸比对（封装请求、响应解析）
func FaceMatch(accessToken string, req MatchRequest) (*MatchResponse, error) {
	url := fmt.Sprintf("https://aip.baidubce.com/rest/2.0/face/v3/match?access_token=%s", accessToken)
	jsonData, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("序列化请求失败: %w", err)
	}

	httpReq, err := http.NewRequest("POST", url, bytes.NewReader(jsonData))
	if err != nil {
		return nil, fmt.Errorf("创建请求失败: %w", err)
	}
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := config.Client.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("发送请求失败: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("读取响应失败: %w", err)
	}

	var matchResp MatchResponse
	if err := json.Unmarshal(body, &matchResp); err != nil {
		return nil, fmt.Errorf("解析响应失败: %w，原始内容: %s", err, string(body))
	}

	if matchResp.ErrorCode != 0 {
		return nil, fmt.Errorf("比对失败 [Code:%d]: %s", matchResp.ErrorCode, matchResp.ErrorMsg)
	}

	return &matchResp, nil
}

// ImageToBase64 将图片转换为Base64编码（通用工具）
func ImageToBase64(image string) (string, error) {
	// 区分网络URL和本地路径
	if strings.HasPrefix(image, "http://") || strings.HasPrefix(image, "https://") {
		// 网络图片：HTTP下载（带10秒超时）
		client := &http.Client{Timeout: 10 * time.Second}
		resp, err := client.Get(image)
		if err != nil {
			return "", fmt.Errorf("下载网络图片失败: %w", err)
		}
		defer resp.Body.Close()

		// 检查HTTP响应状态
		if resp.StatusCode != http.StatusOK {
			return "", fmt.Errorf("HTTP响应异常 [状态码:%d]: %s", resp.StatusCode, image)
		}
		// 读取图片内容
		data, err := io.ReadAll(resp.Body)
		if err != nil {
			return "", fmt.Errorf("读取网络图片内容失败: %w", err)
		}
		// 编码为Base64
		return base64.StdEncoding.EncodeToString(data), nil
	} else {
		// 本地图片：直接读取文件
		file, err := os.Open(image)
		if err != nil {
			return "", fmt.Errorf("打开本地图片失败: %w", err)
		}
		defer file.Close()

		data, err := io.ReadAll(file)
		if err != nil {
			return "", fmt.Errorf("读取本地图片内容失败: %w", err)
		}

		return base64.StdEncoding.EncodeToString(data), nil
	}
}

type BaiduFacesetResp struct {
	ErrorCode int    `json:"error_code"`
	ErrorMsg  string `json:"error_msg"`
	LogId     int64  `json:"log_id"`
	Timestamp int64  `json:"timestamp"`
	Result    struct {
		FaceToken string `json:"face_token"` // 人脸唯一标识（关键，需存本地）
		UserAdded int    `json:"user_added"` // 用户创建结果（0=已存在，1=新增）
		FaceAdded int    `json:"face_added"` // 人脸添加结果（0=已存在，1=新增）
		UserInfo  struct {
			UserId  string `json:"user_id"`  // 百度用户ID（与系统UserId一致）
			GroupId string `json:"group_id"` // 所属用户组
		} `json:"user_info"`
	} `json:"result"`
}

// CreateBaiduGroup 创建百度用户组（若不存在）
func CreateBaiduGroup(accessToken, groupId string) error {
	urlStr := fmt.Sprintf("https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/add?access_token=%s", accessToken)

	formData := url.Values{}
	formData.Set("group_id", groupId)
	// 百度API要求group_name参数，即使与group_id相同
	formData.Set("group_name", groupId)
	formData.Set("group_desc", "smart health platform user group")

	resp, err := http.Post(urlStr, "application/x-www-form-urlencoded", strings.NewReader(formData.Encode()))
	if err != nil {
		return fmt.Errorf("创建用户组失败: %w", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	// 使用更灵活的解析方式
	var groupResp map[string]interface{}
	if err := json.Unmarshal(body, &groupResp); err != nil {
		return fmt.Errorf("解析用户组响应失败: %w, 内容: %s", err, string(body))
	}

	// 检查错误码 - 223101表示组已存在，这是可以接受的
	if errorCode, ok := groupResp["error_code"].(float64); ok {
		// 允许的错误码：0=成功，222207=组已存在，223101=组已存在
		if errorCode != 0 && errorCode != 222207 && errorCode != 223101 {
			errorMsg, _ := groupResp["error_msg"].(string)
			return fmt.Errorf("百度组创建报错 [Code:%.0f]: %s", errorCode, errorMsg)
		}
	}

	pkg.Info("百度用户组创建/检查完成", zap.String("group_id", groupId))
	return nil
}

// 向百度用户组添加用户（并关联人脸）
func AddFaceToBaidu(accessToken, groupId string, userId int32, faceBase64 string) (string, error) {
	urlStr := fmt.Sprintf("https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add?access_token=%s", accessToken)

	formData := url.Values{}
	formData.Set("group_id", groupId)
	formData.Set("user_id", fmt.Sprintf("%d", userId))
	formData.Set("user_info", fmt.Sprintf("user_%d", userId))
	formData.Set("image", faceBase64)
	formData.Set("image_type", "BASE64")
	formData.Set("quality_control", "NORMAL")
	formData.Set("liveness_control", "NONE")

	resp, err := http.Post(urlStr, "application/x-www-form-urlencoded", strings.NewReader(formData.Encode()))
	if err != nil {
		return "", fmt.Errorf("添加人脸到百度失败: %w", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	var faceResp BaiduFacesetResp
	if err := json.Unmarshal(body, &faceResp); err != nil {
		return "", fmt.Errorf("解析百度人脸响应失败: %w, 内容: %s", err, string(body))
	}

	if faceResp.ErrorCode != 0 {
		return "", fmt.Errorf("百度人脸添加报错 [Code:%d]: %s", faceResp.ErrorCode, faceResp.ErrorMsg)
	}

	return faceResp.Result.FaceToken, nil
}

// 3. 从百度用户删除指定人脸
func DeleteFaceFromBaidu(accessToken, groupId string, userId int32, faceToken string) error {
	urlStr := fmt.Sprintf("https://aip.baidubce.com/rest/2.0/face/v3/faceset/face/delete?access_token=%s", accessToken)

	formData := url.Values{}
	formData.Set("group_id", groupId)
	formData.Set("user_id", fmt.Sprintf("%d", userId))
	formData.Set("face_token", faceToken)

	resp, err := http.Post(urlStr, "application/x-www-form-urlencoded", strings.NewReader(formData.Encode()))
	if err != nil {
		return fmt.Errorf("删除百度人脸失败: %w", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	var delResp BaiduFacesetResp
	if err := json.Unmarshal(body, &delResp); err != nil {
		return fmt.Errorf("解析百度删除响应失败: %w, 内容: %s", err, string(body))
	}

	if delResp.ErrorCode != 0 {
		return fmt.Errorf("百度人脸删除报错 [Code:%d]: %s", delResp.ErrorCode, delResp.ErrorMsg)
	}
	return nil
}

// 百度人脸库检索（无账号时，通过人脸查用户）
func SearchFaceInBaidu(accessToken, groupId, faceBase64 string) (int32, float64, error) {
	urlStr := fmt.Sprintf("https://aip.baidubce.com/rest/2.0/face/v3/search?access_token=%s", accessToken)

	formData := url.Values{}
	formData.Set("group_id_list", groupId)
	formData.Set("image", faceBase64)
	formData.Set("image_type", "BASE64")
	formData.Set("quality_control", "NORMAL")

	resp, err := http.Post(urlStr, "application/x-www-form-urlencoded", strings.NewReader(formData.Encode()))
	if err != nil {
		return 0, 0, fmt.Errorf("百度人脸检索失败: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return 0, 0, fmt.Errorf("读取检索响应体失败: %w", err)
	}

	type SearchResp struct {
		ErrorCode int    `json:"error_code"`
		ErrorMsg  string `json:"error_msg"`
		Result    struct {
			UserList []struct {
				UserId    string  `json:"user_id"`
				Score     float64 `json:"score"`
				FaceToken string  `json:"face_token"`
			} `json:"user_list"`
		} `json:"result"`
	}
	var searchResp SearchResp
	if err := json.Unmarshal(body, &searchResp); err != nil {
		return 0, 0, fmt.Errorf("解析检索响应失败: %w, 内容: %s", err, string(body))
	}
	if searchResp.ErrorCode != 0 {
		return 0, 0, fmt.Errorf("百度检索报错 [Code:%d]: %s", searchResp.ErrorCode, searchResp.ErrorMsg)
	}

	if len(searchResp.Result.UserList) == 0 {
		return 0, 0, errors.New("未检索到匹配用户")
	}
	topUser := searchResp.Result.UserList[0]
	if topUser.Score < 80 { // 百度推荐相似度阈值，可调整
		return 0, topUser.Score, errors.New("检索相似度未达标")
	}

	sysUserId, err := strconv.ParseInt(topUser.UserId, 10, 32)
	if err != nil {
		return 0, 0, fmt.Errorf("百度UserID转换失败: %w", err)
	}
	return int32(sysUserId), topUser.Score, nil
}

// Face 人脸比对主逻辑（调用方无需关心图片来源）
func FaceLogin(userId int32, inputImage string) (int32, error) {
	//初始化客户端
	inits.NewBaiduFaceClient()
	// 1. 获取AccessToken
	accessToken, err := GetAccessToken()
	if err != nil {
		pkg.Error("获取AccessToken失败", zap.Int32("user_id", userId), zap.Error(err))
		return 0, err
	}
	// 2. 转换输入图片为Base64
	inputBase64, err := ImageToBase64(inputImage)
	if err != nil {
		pkg.Error("转换输入人脸失败", zap.Int32("user_id", userId), zap.Error(err))
		return 0, err
	}
	// 3. 查询该用户下所有本地人脸
	var fase model.Fase
	fase.UserId = userId
	userFaces, err := fase.GetFasesByUserId()
	if err != nil {
		pkg.Error("查询用户人脸列表失败", zap.Int32("user_id", userId), zap.Error(err))
		return 0, err
	}
	if len(userFaces) == 0 {
		return 0, errors.New("该用户未添加任何人脸")
	}
	// 4. 遍历所有人脸，逐一比对（只要有一张达标即成功）
	for _, face := range userFaces {
		// 转换本地人脸为Base64
		localBase64, err := ImageToBase64(face.Image)
		if err != nil {
			pkg.Warn("转换本地人脸失败，跳过该张", zap.Int32("fase_id", face.ID), zap.Error(err))
			continue
		}
		// 构造比对请求
		reqData := MatchRequest{
			{Image: localBase64, ImageType: "BASE64", QualityControl: "NORMAL"},
			{Image: inputBase64, ImageType: "BASE64", QualityControl: "NORMAL"},
		}
		// 执行人脸比对
		matchResp, err := FaceMatch(accessToken, reqData)
		if err != nil {
			pkg.Warn("单张人脸比对失败，跳过", zap.Int32("fase_id", face.ID), zap.Error(err))
			continue
		}
		// 相似度达标（使用百度推荐阈值）
		if matchResp.Result.Score >= matchResp.Result.Threshold {
			pkg.Info("人脸登录成功", zap.Int32("user_id", userId), zap.Int32("fase_id", face.ID), zap.Float64("score", matchResp.Result.Score))
			return userId, nil
		}
	}
	// 所有人脸均不匹配
	pkg.Warn("所有人脸比对均不达标", zap.Int32("user_id", userId))
	return 0, errors.New("人脸比对失败，无匹配记录")
}

const BaiduFaceGroup = "smart_health_group"

// AddFaseByUserId 新增用户人脸（支持多张，同步百度人脸库）
func AddFaseByUserId(userId int32, image string) error {
	startTime := time.Now()

	// 1. 获取带缓存的AccessToken
	accessToken, err := GetAccessTokenWithCache()
	if err != nil {
		pkg.Error("获取AccessToken失败", zap.Int32("user_id", userId), zap.Error(err))
		return err
	}

	// 2. 初始化百度用户组（确保组存在）- 这里会处理223101错误
	if err := CreateBaiduGroup(accessToken, BaiduFaceGroup); err != nil {
		pkg.Error("初始化百度用户组失败", zap.Error(err))
		return err
	}

	// 3. 图片转Base64
	faceBase64, err := ImageToBase64(image)
	if err != nil {
		pkg.Error("图片转Base64失败", zap.Int32("user_id", userId), zap.Error(err))
		return err
	}

	// 4. 新增到百度人脸库，获取FaceToken
	faceToken, err := AddFaceToBaidu(accessToken, BaiduFaceGroup, userId, faceBase64)
	if err != nil {
		pkg.Error("添加人脸到百度失败", zap.Int32("user_id", userId), zap.Error(err))
		return err
	}

	// 5. 新增到本地数据库
	fase := &model.Fase{
		UserId:    userId,
		Image:     image,
		FaceToken: faceToken,
	}
	if err := fase.AddFase(); err != nil {
		// 数据库失败时，回滚百度操作
		_ = DeleteFaceFromBaidu(accessToken, BaiduFaceGroup, userId, faceToken)
		pkg.Error("新增本地人脸记录失败", zap.Int32("user_id", userId), zap.Error(err))
		return err
	}

	duration := time.Since(startTime)
	pkg.Info("人脸新增成功",
		zap.Int32("user_id", userId),
		zap.String("face_token", faceToken),
		zap.Duration("duration", duration))

	return nil
}
func UpdateFase(userId int, image string) error {
	var fase model.Fase
	fase.UserId = int32(userId)
	// 步骤1：检查用户是否已有未删除的人脸记录
	if err := fase.GetFaseByUserId(); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			pkg.Error("用户未添加人脸信息，无法修改", zap.Error(err))
			return errors.New("用户未添加人脸信息，无法修改")
		}
		pkg.Error("查询用户人脸记录失败", zap.Error(err))
		return errors.New("查询用户人脸记录失败")
	}
	// 步骤2：若查询结果为空（ID为0，代表无有效记录）
	if fase.ID == 0 {
		pkg.Error("用户未添加人脸信息，无法修改")
		return errors.New("用户未添加人脸信息，无法修改")
	}
	// 步骤3：执行更新（仅更新 image 和 updated_at）
	fase.Image = image
	err := fase.UpdateFase()
	if err != nil {
		pkg.Error("更改失败", zap.Error(err))
		return err
	}
	return nil
}
func DeletedFase(userId int, faseId int32) error {
	// 1. 查本地人脸记录（含百度FaceToken）
	var fase model.Fase
	fase.UserId = int32(userId)
	fase.ID = faseId
	if err := fase.GetFaseById(); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.New("该人脸记录不存在")
		}
		return fmt.Errorf("查询本地人脸失败: %w", err)
	}

	// 2. 获取AccessToken
	accessToken, err := GetAccessToken()
	if err != nil {
		pkg.Error("获取AccessToken失败", zap.Int("user_id", userId), zap.Error(err))
		return err
	}
	// 3. 先删除百度人脸库的记录
	if err := DeleteFaceFromBaidu(accessToken, BaiduFaceGroup, int32(userId), fase.FaceToken); err != nil {
		pkg.Error("删除百度人脸失败", zap.Int("user_id", userId), zap.String("face_token", fase.FaceToken), zap.Error(err))
		return err
	}
	// 4. 再删除本地数据库记录（软删除）
	if err := fase.DeletedFase(); err != nil {
		// 本地删除失败，尝试恢复百度人脸（尽力保证一致）
		faceBase64, _ := ImageToBase64(fase.Image)
		_, _ = AddFaceToBaidu(accessToken, BaiduFaceGroup, int32(userId), faceBase64)
		pkg.Error("删除本地人脸失败", zap.Int("user_id", userId), zap.Error(err))
		return err
	}

	pkg.Info("人脸删除成功（本地+百度）", zap.Int("user_id", userId), zap.Int32("fase_id", faseId))
	return nil
}
